package internal

import (
	"fmt"
	global2 "longmen/server/config/global"
	"longmen/server/pkg/common/helper/const"
	"longmen/server/pkg/common/helper/util"
	"longmen/server/pkg/common/services"
	"longmen/server/pkg/db/mysql"
	"longmen/server/pkg/db/mysql/db_main"
	"longmen/server/pkg/db/mysql/models/models_main"
	cache2 "longmen/server/pkg/db/redis/cache"
	"time"

	"github.com/google/uuid"
	"github.com/streadway/amqp"
)

/**
 * @Author admin
 * @Description 只有长连接消费模式CHANNEL  不用关闭,其它单次使用需要关闭
 * @Date 17:47 2022/9/30
 * @Param
 * @return
 **/
func MqGiftConsumer() {
	for {
		mqGift := global2.GetGiftRabbitMQ()
		channel := mqGift.OpenChannel()
		closeChannelChan := make(chan *amqp.Error, 1)
		closeConnChan := make(chan *amqp.Error, 1)
		notifyChannelClose := channel.NotifyClose(closeChannelChan)    //一旦消费者的channel有错误，产生一个amqp.Error，channel监听并捕捉到这个错误
		notifyConnClose := mqGift.GetConn().NotifyClose(closeConnChan) //一旦消费者的conn有错误，产生一个amqp.Error，conn监听并捕捉到这个错误
		channel.Qos(
			5,     // prefetch count
			0,     // prefetch size
			false, // global
		)
		closeFlag := false
		messages, err := mqGift.RecieveRoutingWithOutBind(channel)
		if err != nil {
			global2.Log.Error("消费礼物失败,请检测应用代码")
		}
		for {
			select {
			case e := <-notifyChannelClose:
				global2.Log.Debugf("chan通道错误,e:%s", e.Error())
				//channel.Cancel("", false)
				closeFlag = true
			case e := <-notifyConnClose:
				global2.Log.Debugf("conn错误,e:%s", e.Error())
				mqGift.ReConn()
				closeFlag = true
			case msg := <-messages:
				go func() {
					//msg.Headers 失败的次数
					//特别注意这里是int32 必须的，不然解析不出来
					//retryNums, ok := msg.Headers["retry_nums"].(int32)
					giftLog := &models_main.GiftLog{}
					giftLogRecordStr := string(msg.Body)
					util.Json2Struct(giftLogRecordStr, giftLog)
					if giftLog.GiftType != _const.ActivityGiftType {
						calculateAnchorProfit(giftLog)
					}
					//分红比例不对，直接进入死信队列
					if giftLog.AnchorProfit <= 0 && giftLog.GiftType != _const.ActivityGiftType {
						msg.Reject(false)
					} else {
						//这里面含失败重试功能
						if flag := commitAnchorAndOtherMq(giftLog); flag {
							//确认必须写成ack false 因为当批量异步处理， 不当前条为是第几条，如果向服务器发送ack true,会确认当前条以前的所有条为true
							msg.Ack(false)
						} else {
							//入死信队列
							msg.Reject(false)
						}
					}
					//	}
				}()
			}
			if closeFlag {
				break
			}
		}
	}
}

/**
 * @Author admin
 * @Description // 启动一个轮循的线程,阻塞10分钟，当有送礼，会自动弹出
 * @Date 18:10 2022/9/28
 * @Param
 * @return
 **/
func QueryRedisGiftToMqOmit() {
	for {
		// 设置为阻赛30秒, 如果设置为0无限阻塞怕假死以及受redis连接时长限制 时间不能设置太长
		if result, err := global2.GetRds().BLPop(30*time.Second, _const.BLOCK_LIST_GIFT_KEY).Result(); err == nil && len(result) > 0 {
			operateId := result[1]
			if giftLogRecord := db_main.QueryByOperateId(operateId); giftLogRecord != nil {
				giftLogRecordStr, _ := util.Struct2Json(giftLogRecord)
				if len(giftLogRecordStr) > 0 {
					services.GiftPublishConfirm(global2.GetGiftRabbitMQ(), giftLogRecordStr, giftLogRecord.OperateId, _const.MQ_FAIL_RETRY_TIME)
				} else {
					//解析失败需要重新存入redis
					global2.GetRds().LPush(_const.BLOCK_LIST_GIFT_KEY, operateId)
				}
			}
			//go SendGiftStep2()
		}
	}
}

/**
 * @Author admin
 * @Description //
 * @Date 13:46 2022/10/3
 * @Param
 * @return
 **/
func QueryDbGiftToMqOmit() {
	giftLogList := db_main.QueryByExpired()
	for _, giftLogRecord := range giftLogList {
		giftLogRecordStr, _ := util.Struct2Json(giftLogRecord)
		services.GiftPublishConfirm(global2.GetGiftRabbitMQ(), giftLogRecordStr, giftLogRecord.OperateId, _const.MQ_FAIL_RETRY_TIME)
	}
}

/**
 * @Author admin
 * @Description //发送礼物第二步
 * @Date 17:47 2022/9/30
 * @Param
 * @return
 **/
func SendGiftStep2() {
	for {
		operateId := uuid.New().String()
		if rows := db_main.UpdateGiftLogOperateId(operateId); rows > 0 {
			list := db_main.GetGiftLogList(operateId)
			for _, giftLog := range list {
				calculateAnchorProfit(giftLog)
				commitAnchorAndOther(giftLog)
			}
		} else {
			break
		}
	}
}

/**
 * @Author admin
 * @Description //TODO
 * @Date 13:32 2022/9/29
 * @Param
 * @return
 **/
func calculateAnchorProfit(giftLog *models_main.GiftLog) {
	if anchor, err := mysql.GetUserParam(map[string]interface{}{"id": giftLog.Anchorid}); err == nil {
		iSharingRatio := anchor.SharingRatio
		if iSharingRatio == 0 {
			global2.Log.Errorf("6SendGift9主播分成设置有错，订单设为最低的1%")
			iSharingRatio = 1
		}
		anchorDiamondGet := giftLog.ActualSpend * iSharingRatio / 100 //假设是鹰钻，那得到的就是这个值
		if giftLog.GiftType != _const.ActivityGiftType {
			//如果计价单位是gold，那么送记人有扣钱，收礼人有加钱，有礼物记录；但是不会有公会分成
			if giftLog.SpendType == int(_const.CoinDiamond) { //anchor.ProfitDiamond > 0 &&
				// 如果主播有工会经纪人
				if anchor.GuildManagerId > 0 {
					//首先只要写入主播赚多少钱就行啦，要把主播上级上上级...分成方案记入到缓存中
					guildRate, plan, err := cache2.GetAnchorSharePlan(anchor.Id)
					if err != nil {
						global2.Log.Errorf("6SendGift9主播公会分成配置出错, 按无公会处理%v", err)
					} else {
						anchorDiamondGet, _ = util.CalcCommissionListByAmount(guildRate, plan, giftLog.ActualSpend)
					}
					if anchorDiamondGet == 0 {
						global2.Log.Infof("6SendGift9主播%d分成得0，公会分成金额设置有错，给主播自己分成", anchor.Id)
						anchorDiamondGet = giftLog.ActualSpend * iSharingRatio / 100
					} //end if主播0分成
				} //end if 有公会
			} //end if 是克拉
		}
		giftLog.AnchorProfit = anchorDiamondGet
	}
}

/**
 * @Author admin
 * @Description //事务处理
 * @Date 13:33 2022/9/29
 * @Param
 * @return
 **/
func commitAnchorAndOther(giftLog *models_main.GiftLog) {
	//加收礼人的钱
	session := global2.GetX().Begin()
	if _, _, err := db_main.UpdateAmountByTx(giftLog.Anchorid, giftLog.AnchorProfit, _const.FundsType(giftLog.SpendType), session, _const.WALLET_BALANCE_HIDE, []*models_main.UserProfit{getAnchorProfitRecord(giftLog)}); err != nil {
		session.Rollback()
	}
	if db_main.UpdateGiftLogById(giftLog.Id, session) == 0 {
		session.Rollback()
	}
	//其它业务例如 计算公会
	sessionLive := global2.Live().Begin()
	if _, err := db_main.InsertLogByTx(sessionLive, giftLog); err != nil {
		session.Rollback()
		sessionLive.Rollback()
	}
	if session.Commit().Error != nil || sessionLive.Commit().Error != nil {
		session.Rollback()
		sessionLive.Rollback()
	}
}

/**
 * @Author admin
 * @Description //事务处理
 * @Date 13:33 2022/9/29
 * @Param
 * @return
 **/
func commitAnchorAndOtherMq(giftLog *models_main.GiftLog) bool {
	//加收礼人的钱
	for i := 0; i < 3; i++ {
		time.Sleep(time.Nanosecond * 300 * time.Duration(i))
		session := global2.GetX().Begin()
		if giftLog.GiftType != _const.ActivityGiftType {
			if _, _, err := db_main.UpdateAmountByTx(giftLog.Anchorid, giftLog.AnchorProfit, _const.FundsType(giftLog.SpendType), session, _const.WALLET_BALANCE_HIDE, []*models_main.UserProfit{getAnchorProfitRecord(giftLog)}); err != nil {
				session.Rollback()
				continue
			}
		}
		//其它业务例如 计算公会
		//sessionLive := global.Live().Begin()
		row := db_main.InsertLogByOperateId(session, giftLog.OperateId, giftLog.AnchorProfit)
		if row == 0 {
			session.Rollback()
			continue
		}
		//重复的KEY,证明是重复的数据，不需要执行需要回退.并需要给回执为真
		if row == 999 {
			session.Rollback()
			return true
		}
		if session.Commit().Error != nil {
			session.Rollback()
			continue
		}
		err := cache2.OnTaskEvent(giftLog.Uid, _const.TASK_NEWBIE_N_GIFT, 1) //送了一个礼
		if err != nil {
			global2.Log.Warnf("taskErr任务出错%v", err)
		}
		return true
	}
	return false
}

func getAnchorProfitRecord(giftLog *models_main.GiftLog) *models_main.UserProfit {
	//写入主播收益记录
	//if gift.SpendType == int(util.CoinDiamond) {
	content := fmt.Sprintf("收到礼物ID:%dx%d 礼物总金额%d%s 实际收礼金额%d%s", giftLog.Giftid, giftLog.Count, giftLog.Spend/100, util.GetCoinName(giftLog.SpendType), giftLog.ActualSpend/100, util.GetCoinName(giftLog.SpendType))
	return &models_main.UserProfit{
		Uid:         giftLog.Anchorid, //记录的主人
		FUid:        giftLog.Uid,      //相关的人（送礼方）
		ConsumeType: _const.PROFIT_COMMISSION_ANCHOR,
		Type:        _const.TYPE_PROFIT, //这是一个赚钱记录
		CoinCount:   giftLog.AnchorProfit,
		Content:     content,
		Resid:       giftLog.Giftid,
		Count:       giftLog.Count,
		CreateTime:  giftLog.CreateTime,
		CoinType:    giftLog.SpendType,
		OrderNo:     util.GenOrderId("GR", 1),
		Liveid:      giftLog.Liveid,
	}
}

func GiftPublishConfirmRetry(giftRabbitMQ *global2.RabbitMQ, message string, retryNums int32) bool {
	channel := giftRabbitMQ.OpenChannel()
	defer channel.Close()
	channel.Confirm(false)
	header := make(map[string]interface{}, 1)
	header["retry_nums"] = retryNums
	confirms := channel.NotifyPublish(make(chan amqp.Confirmation, 1)) // 处理确认逻辑
	//2.发送消息
	err := channel.Publish(
		giftRabbitMQ.Exchange,
		//要设置
		giftRabbitMQ.Key,
		false,
		false,
		amqp.Publishing{
			ContentType: "text/plain",
			Body:        []byte(message),
			Headers:     header,
		})
	//Headers Table 这个里面可以设置消费次数
	if err != nil {
		global2.Log.Debug(err)
	}
	if confirmed := <-confirms; confirmed.Ack {
		return true
	} else {
		return false
	}
}
